13. Using Idling Resources

L5 P4 A11 Using Idling Resources V2

In this step, you'll use your two idling resources to make your TasksActivityTest end-to-end tests run deterministically.

Step 1: Use Idling Resources in Tests

  1. Open TasksActivityTest.kt.
  2. Instantiate a private DataBindingIdlingResource:

TasksActivityTest.kt

// An idling resource that waits for Data Binding to have no pending bindings.
private val dataBindingIdlingResource = DataBindingIdlingResource()
  1. Create @Before and @After methods that register and unregister the EspressoIdlingResource.countingIdlingResource and dataBindingIdlingResource:

TasksActivityTest.kt

/**
 * Idling resources tell Espresso that the app is idle or busy. This is needed when operations
 * are not scheduled in the main Looper (for example when executed on a different thread).
 */
@Before
fun registerIdlingResource() {
    IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
    IdlingRegistry.getInstance().register(dataBindingIdlingResource)
}

/**
 * Unregister your Idling Resource so it can be garbage collected and does not leak any memory.
 */
@After
fun unregisterIdlingResource() {
    IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
    IdlingRegistry.getInstance().unregister(dataBindingIdlingResource)
}
  1. Update the editTask() test so that after you launch the activity scenario, you use monitorActivity to associate the activity with the dataBindingIdlingResource:

TasksActivityTest.kt

@Test
fun editTask() = runBlocking {
    repository.saveTask(Task("TITLE1", "DESCRIPTION"))

    // Start up Tasks screen.
    val activityScenario = ActivityScenario.launch(TasksActivity::class.java)
    dataBindingIdlingResource.monitorActivity(activityScenario) // LOOK HERE

    // Rest of test...
}
  1. Run your test five times. You should find that the test is no longer flaky.

The entire TasksActivityTest should look like this:

TasksActivityTest.kt

@RunWith(AndroidJUnit4::class)
@LargeTest
class TasksActivityTest {

    private lateinit var repository: TasksRepository

    // An idling resource that waits for Data Binding to have no pending bindings.
    private val dataBindingIdlingResource = DataBindingIdlingResource()

    @Before
    fun init() {
        repository =
            ServiceLocator.provideTasksRepository(
                getApplicationContext()
            )
        runBlocking {
            repository.deleteAllTasks()
        }
    }

    @After
    fun reset() {
        ServiceLocator.resetRepository()
    }

    /**
     * Idling resources tell Espresso that the app is idle or busy. This is needed when operations
     * are not scheduled in the main Looper (for example when executed on a different thread).
     */
    @Before
    fun registerIdlingResource() {
        IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
        IdlingRegistry.getInstance().register(dataBindingIdlingResource)
    }

    /**
     * Unregister your Idling Resource so it can be garbage collected and does not leak any memory.
     */
    @After
    fun unregisterIdlingResource() {
        IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
        IdlingRegistry.getInstance().unregister(dataBindingIdlingResource)
    }

    @Test
    fun editTask() = runBlocking {

        // Set initial state.
        repository.saveTask(Task("TITLE1", "DESCRIPTION"))

        // Start up Tasks screen.
        val activityScenario = ActivityScenario.launch(TasksActivity::class.java)

        // Click on the task on the list and verify that all the data is correct.
        onView(withText("TITLE1")).perform(click())
        onView(withId(R.id.task_detail_title_text)).check(matches(withText("TITLE1")))
        onView(withId(R.id.task_detail_description_text)).check(matches(withText("DESCRIPTION")))
        onView(withId(R.id.task_detail_complete_checkbox)).check(matches(not(isChecked())))

        // Click on the edit button, edit, and save.
        onView(withId(R.id.edit_task_fab)).perform(click())
        onView(withId(R.id.add_task_title_edit_text)).perform(replaceText("NEW TITLE"))
        onView(withId(R.id.add_task_description_edit_text)).perform(replaceText("NEW DESCRIPTION"))
        onView(withId(R.id.save_task_fab)).perform(click())

        // Verify task is displayed on screen in the task list.
        onView(withText("NEW TITLE")).check(matches(isDisplayed()))
        // Verify previous task is not displayed.
        onView(withText("TITLE1")).check(doesNotExist())
        // Make sure the activity is closed before resetting the db.
        activityScenario.close()
    }

}

Step 2: Write your own test with idling resources

Now it's your turn.

  1. Copy over the following code:

TasksActivityTest.kt
````
@Test
fun createOneTask_deleteTask() {

// 1. Start TasksActivity.

// 2. Add an active task by clicking on the FAB and saving a new task.

// 3. Open the new task in a details view.

// 4. Click delete task in menu.

// 5. Verify it was deleted.

// 6. Make sure the activity is closed.

}
```

  1. Finish the code, referring to the editTask test you added previously.
  2. Run your test and confirm it passes!

The completed test is here so you can compare.